#include "buffer.h"

Buffer::Buffer(int initBufferSize) : buffer_(initBufferSize), readPos_(0), writePos_(0)
{
}

size_t Buffer::ReadableBytes() const
{
    return writePos_ - readPos_;
}

size_t Buffer::WriteableBytes() const
{
    return buffer_.size() - writePos_;
}

size_t Buffer::PrependableBytes() const
{
    return readPos_;
}

char *Buffer::BeginPtr()
{
    return &(*this->buffer_.begin());
}

const char *Buffer::BeginPtr() const
{
    return &(*this->buffer_.begin());
}

char *Buffer::BeginRead()
{
    return BeginPtr() + readPos_;
}

const char *Buffer::BeginReadConst() const
{
    return BeginPtr() + readPos_;
}

char *Buffer::BeginWrite()
{
    return BeginPtr() + writePos_;
}

const char *Buffer::BeginWrite() const
{
    return BeginPtr() + writePos_;
}

void Buffer::Retrieve(int len)
{
    assert(len <= ReadableBytes());
    readPos_ += len;
}

void Buffer::RetrieveUntil(const char *end)
{
    assert(BeginRead() <= end);
    Retrieve(end - BeginRead());
}

void Buffer::RetrieveAll()
{
    bzero(&buffer_[0], buffer_.size());
    readPos_ = 0;
    writePos_ = 0;
}

void Buffer::HasWrite(size_t len)
{
    writePos_ += len;
}

void Buffer::EnsureWriteable(size_t len)
{
    if (WriteableBytes() < len)
    {
        MakeSpace(len);
    }
    assert(WriteableBytes() >= len);
}

void Buffer::MakeSpace(size_t len)
{
    if (WriteableBytes() + PrependableBytes() < len)
    {
        buffer_.resize(writePos_ + len + 1);
    }
    else
    {
        size_t readable = ReadableBytes();
        std::copy(BeginRead(), BeginWrite(), BeginPtr());
        readPos_ = 0;
        writePos_ = readable;
    }
}

std::string Buffer::RetrieveAllToString()
{
    std::string str(BeginRead(), ReadableBytes());
    RetrieveAll();
    return str;
}

void Buffer::Append(const char *str, size_t len)
{
    assert(str);
    EnsureWriteable(len);
    std::copy(str, str + len, BeginWrite());
    HasWrite(len);
}

void Buffer::Append(const std::string &str)
{
    Append(str.c_str(), str.size());
}

void Buffer::Append(const void *data, size_t len)
{
    Append(static_cast<const char *>(data), len);
}

void Buffer::Append(const Buffer &buffer)
{
    Append(buffer.BeginReadConst(), buffer.ReadableBytes());
}

ssize_t Buffer::ReadFd(int fd, int *Errno)
{
    char buf[MAX_SOCKT_BUFFER_SIZE];
    struct iovec iov[2];
    const size_t writeabel = WriteableBytes();

    iov[0].iov_base = BeginWrite();
    iov[0].iov_len = writeabel;
    iov[1].iov_base = buf;
    iov[1].iov_len = MAX_SOCKT_BUFFER_SIZE;

    ssize_t size = readv(fd, iov, 2);
    if (size < 0)
    {
        *Errno = errno;
    }
    else if (size <= WriteableBytes())
    {
        HasWrite(size);
    }
    else
    {
        HasWrite(WriteableBytes());
        Append(buf, size - writeabel);
    }
    return size;
}

ssize_t Buffer::WriteFd(int fd, int *Errno)
{
    size_t readSize = WriteableBytes();
    ssize_t writeSize = write(fd, BeginRead(), readSize);
    if(writeSize < 0) {
        *Errno = errno;
        return writeSize;
    }
    Retrieve(writeSize);
    return writeSize;
}
